home *** CD-ROM | disk | FTP | other *** search
- /*-------------------------------------------------------------------
-
- AOCE Post Office Protocol (POP) / Simple Mail Transfer Protocol (SMTP)
- Mail Service Access Module
-
- written by Steve Falkenburg-- MacDTS
- ©1991-1993 Apple Computer, Inc.
-
- --------------
- change history
- --------------
-
- SJF 02/19/93 update for beta build b1
- SJF 10/29/92 update to a11 a11
- SJF 06/08/92 update to a8 a8
- SJF 02/15/92 first working version a4.5
- SJF 10/16/91 initial coding a3
-
- ---------------------------------------------------------------------*/
-
- #ifndef __TRAPS__
- #include <Traps.h>
- #endif
-
- #ifndef __GESTALTEQU__
- #include <GestaltEqu.h>
- #endif
-
- #ifndef __OCE__
- #include <OCE.h>
- #endif
-
- #ifndef __OCEAUTHDIR__
- #include <OCEAuthDir.h>
- #endif
-
- #ifndef __OCEERRORS__
- #include <OCEErrors.h>
- #endif
-
- #include <string.h>
-
- #ifdef applec
- #include <strings.h>
- #endif
-
- #include "const.h"
- #include "gwerrors.h"
- #include "mytypes.h"
- #include "globals.h"
- #include "utils.h"
- #include "trapavailable.h"
- #include "authstuff.h"
- #include "gatewaystuff.h"
-
-
- // think c compatibility
- #ifndef _GestaltDispatch
- #define _GestaltDispatch _Gestalt
- #endif
-
-
- // InitGatewayStuff
- //
- // called when the gateway is first launched. initializes the global variables of importance
- // to AOCE and reads in the slot information for all of the gateways slots.
- //
- OSErr InitGatewayStuff(void)
- {
- OSErr err;
- DirParamBlock dirBlock;
- MSAMParam msBlock;
-
- gHLEventAdd = gHLEventRemove = nil;
-
- gNumSlots = 0;
-
- // get the local identity for dir manager calls
-
- err = InitAuthStuff();
- if (err!=noErr)
- return err;
-
- // get creation ID of the AOCE setup PAB
-
- err = DirGetOCESetupRefNum(&dirBlock,false);
- if (err!=noErr) {
- ExitAuthStuff();
- return err;
- }
-
- OCECopyCreationID(&dirBlock.dirGetOCESetupRefNumPB.oceSetupRecordCID,&gAOCESetupCID);
- gAOCESetupDSRef = dirBlock.dirGetOCESetupRefNumPB.dsRefNum;
-
- // get location
-
- err = GetSingleValueAttribute(&gAOCESetupCID,OCEGetIndAttributeType(kLocationAttrTypeNum),
- &gLocation,sizeof(OCESetupLocation));
- if (err!=noErr)
- return err;
-
- // get creation ID of gateway setup record
-
- err = PMSAMGetMSAMRecord(&msBlock);
- if (err!=noErr) {
- ExitAuthStuff();
- return err;
- }
- OCECopyCreationID(&msBlock.pmsamGetMSAMRecord.msamCID,&gMSAMCID);
-
- // read slot info
-
- err = ReadGatewaySlotInformation();
- if (err==kMailNoSuchSlot) {
- #ifdef kDEBUG
- DebugStr("\pQueue not registered yet, but we'll continue anyway, since it will be sometime");
- #endif
- MarkSlotInformationDirty();
- err = noErr;
- }
-
- if (err!=noErr) {
- ExitAuthStuff();
- return err;
- }
-
- gAOCEInited = true;
-
- return err;
- }
-
-
- // ActivateSlot
- //
- // sets up a slot spec as active and ready to accept mail, this call gets refs to the in and out
- // queues for the slot
- //
- OSErr ActivateSlot(SlotSpec *slot)
- {
- MSAMParam msParam;
- OSErr err;
-
- // get slot queues
-
- msParam.pmsamOpenQueues.msamSlotID = slot->slotID;
- err = PMSAMOpenQueues(&msParam);
-
- // set up other slot info
-
- slot->inQueue = msParam.pmsamOpenQueues.inQueueRef;
- slot->outQueue = msParam.pmsamOpenQueues.outQueueRef;
- slot->lastCheckGet = 0;
- slot->lastCheckPut = 0;
- slot->enabled = true;
- slot->retryPut = 0;
- slot->retryGet = 0;
- slot->retryPutError = 0;
- slot->retryGetError = 0;
-
- if (err!=noErr) {
- slot->enabled = false;
- }
-
- return err;
- }
-
-
- // CheckSlotRefresh
- //
- // called when we need to see if the slot information needs to be updated. calls
- // ReadGatewaySlotInformation to re-read the information
- //
- OSErr CheckSlotRefresh(void)
- {
- OSErr err;
-
- err = noErr;
-
- if (gUpdateSlotInformation) {
- err = ReadGatewaySlotInformation();
- if (err==kMailNoSuchSlot) {
- // no queue for our slot yet... try again later.
- err = noErr;
- #ifdef kDEBUG
- DebugStr("\pno queue yet... try again later");
- #endif
- MarkSlotInformationDirty();
- }
- }
- return err;
- }
-
-
- // MarkSlotInformationDirty
- //
- // marks the slot database for refresh at main event loop time
- //
- void MarkSlotInformationDirty(void)
- {
- gUpdateSlotInformation = true;
- }
-
-
- // ReadGatewaySlotInformation
- //
- // called in response to a launch *or* in response to a change in the slot database,
- // this call rebuilds the internal database of slots from the AOCE Setup Directory
- //
- OSErr ReadGatewaySlotInformation(void)
- {
- short slotIndex;
- OSErr err;
-
- TraceExecution("\pReadGatewaySlotInformation");
-
- gUpdateSlotInformation = false;
- gNumSlots = 0;
-
- // set up the skeleton slot database containing only the CID to the mailservice records
-
- err = GetParseAttributes(&gMSAMCID,kMailServiceAttrTypeNum,AddSlotCallback,0L);
- if (err!=noErr)
- return err;
-
- // for each slot, get the slot data from the mailservice record and activate the slot
-
- for (slotIndex=0;slotIndex<gNumSlots && err==noErr;slotIndex++) {
- err = GetMailServiceInfo(&gSlotDatabase[slotIndex].slotCID,&gSlotDatabase[slotIndex]);
- if (err==noErr)
- err = ActivateSlot(&gSlotDatabase[slotIndex]);
- }
-
- gAuthRefresh = true; // re-read the directory external identities
-
- if (gAOCEInited && (gNumSlots==0)) { // if we don't have any slots, and we didn't just launch,
- gDone = true; // then quit MSAM
- #ifdef kDEBUG
- DebugStr("\pexiting since no slots");
- #endif
- }
-
- return err;
- }
-
-
- // GetMailServiceInfo
- //
- // given a creation ID to a mailservice record ID and a storage area in the slot database, this function fills
- // in the slot database information for a particular slot.
- //
- OSErr GetMailServiceInfo(CreationID *cid,SlotSpecPtr theSlot)
- {
- PackedRecordID packedAssocDir;
- RecordID assocDir;
- CreationID *dirCID;
- OSErr err;
- OSType dirType;
- RString comment;
- AttributeType attrTypeString;
-
- // get slot id
-
- err = GetSingleValueAttribute(cid,OCEGetIndAttributeType(kSlotIDAttrTypeNum),&theSlot->slotID,sizeof(short));
- if (err!=noErr)
- return err;
-
- // get standard slot information
-
- err = GetSingleValueAttribute(cid,OCEGetIndAttributeType(kStdSlotInfoAttrTypeNum),&theSlot->stdInfo,sizeof(MailStandardSlotInfoAttribute));
- if (err!=noErr)
- return err;
- theSlot->locationActive = theSlot->stdInfo.active;
-
- // get associated directory
-
- err = GetSingleValueAttribute(cid,OCEGetIndAttributeType(kAssoDirectoryAttrTypeNum),&packedAssocDir,sizeof(PackedRecordID));
- if (err!=noErr)
- return err;
-
- // get info from associated directory
-
- OCEUnpackRecordID(&packedAssocDir,&assocDir);
- dirCID = &assocDir.local.cid;
-
- // get directory type
-
- err = GetSingleValueAttribute(dirCID,OCEGetIndAttributeType(kDirTypeAttrTypeNum),&dirType,sizeof(OSType));
- if (err!=noErr)
- return err;
-
- // get directory name
-
- err = GetSingleValueAttribute(dirCID,OCEGetIndAttributeType(kRealNameAttrTypeNum),&theSlot->directoryName,sizeof(RString));
- if (err!=noErr)
- return err;
-
- // get directory discriminator
-
- err = GetSingleValueAttribute(dirCID,OCEGetIndAttributeType(kDiscriminatorAttrTypeNum),
- &theSlot->discriminator,sizeof(DirDiscriminator));
- if (err!=noErr)
- return err;
-
- // get directory comment
-
- err = GetSingleValueAttribute(dirCID,OCEGetIndAttributeType(kCommentAttrTypeNum),&comment,sizeof(RString));
- if (err!=noErr)
- return err;
-
- //
- // code to get specific slot information
- //
-
- // now we get all the specific information at the same time
-
- OCECToRString(kSpecInfoAttr,smRoman,(RString*)&attrTypeString,kRStringMaxBytes);
- err = GetSingleValueAttribute(cid,&attrTypeString,&theSlot->specInfo,sizeof(SpecificSlotInfo));
- if (err!=noErr)
- return err;
-
- // note that we don't get the name and password -- these are filled in by the auth mgr
- // refresh mechanism, dispatched by setting a flag from the auth mgr queue notification
- // proc, and filled in at event loop time
-
- theSlot->dirIdentity.userName[0] = 0;
- theSlot->dirIdentity.password[0] = 0;
- theSlot->dirIdentity.valid = false;
-
- return err;
- }
-
-
- // GetParseAttributes
- //
- // this routine provides a generic lookup routine to iterate through attributes in a
- // personal directory record. it's used to read the slot setup information out of the
- // aoce setup directory
- //
- OSErr GetParseAttributes(CreationID *cid,short attrType,ForEachAttrValue callBack,long clientData)
- {
- DirParamBlock dirBlock;
- Ptr buff;
- Boolean moreData;
- OSErr err;
- RecordIDPtr recordList[1];
- AttributeTypePtr attrList[1];
- RecordID recordID;
-
- MakePersonalRecordID(&recordID,cid);
-
- recordList[0] = (RecordIDPtr)&recordID;
- attrList[0] = OCEGetIndAttributeType(attrType);
- buff = NewPtrChk(kAttrBufferSize);
- if (MemError()!=noErr)
- return MemError();
-
- ClearBuffer(&dirBlock,sizeof(DirParamBlock));
-
- dirBlock.header.clientData = clientData;
- dirBlock.lookupGetPB.serverHint.aNet = 0;
- dirBlock.lookupGetPB.serverHint.aNode = 0;
- dirBlock.lookupGetPB.serverHint.aSocket = 0;
- dirBlock.lookupGetPB.dsRefNum = gAOCESetupDSRef;
- dirBlock.lookupGetPB.identity = gLocalIdentity;
- dirBlock.lookupGetPB.aRecordList = recordList;
- dirBlock.lookupGetPB.attrTypeList = attrList;
- dirBlock.lookupGetPB.recordIDCount = 1;
- dirBlock.lookupGetPB.attrTypeCount = 1;
- dirBlock.lookupGetPB.includeStartingPoint = false;
- dirBlock.lookupGetPB.getBuffer = buff;
- dirBlock.lookupGetPB.getBufferSize = kAttrBufferSize;
- dirBlock.lookupGetPB.startingRecordIndex = 1;
- dirBlock.lookupGetPB.startingAttrTypeIndex = 1;
- dirBlock.lookupParsePB.eachRecordID = nil;
- dirBlock.lookupParsePB.eachAttrType = nil;
- dirBlock.lookupParsePB.eachAttrValue = callBack;
- OCESetCreationIDtoNull(&dirBlock.lookupGetPB.startingAttribute.cid);
-
- do {
- err = DirLookupGet(&dirBlock,false);
- if (err==kOCEMoreData)
- moreData = true;
- else
- moreData = false;
- if (err==noErr || err==kOCEMoreData) {
- err = DirLookupParse(&dirBlock,false);
- if (err!=noErr)
- moreData = false; // this could be kOCEMoreAttrValue (buffer not big enough)
- // or kOCEMoreData (didn't have permission to read)
- }
- } while (moreData);
-
- DisposPtrChk(buff);
-
- return err;
- }
-
-
- // GetSingleValueAttribute
- //
- // this routine returns a single attribute value from a record. it's used to get information out
- // of the slot setup information
- //
- OSErr GetSingleValueAttribute(CreationID *cid,AttributeType *attribType,void *attrBuffer,Size attrBufferSize)
- {
- DirParamBlock dirBlock;
- Ptr buff;
- OSErr err;
- RecordIDPtr recordList[1];
- AttributeTypePtr attrList[1];
- RecordID recordID;
- AttributeCopyInfo callbackInfo;
-
- callbackInfo.buffer = attrBuffer;
- callbackInfo.bufferSize = attrBufferSize;
-
- MakePersonalRecordID(&recordID,cid);
-
- recordList[0] = (RecordIDPtr)&recordID;
- attrList[0] = attribType;
- buff = NewPtrChk(kAttrBufferSize);
- if (MemError()!=noErr)
- return MemError();
-
- ClearBuffer(&dirBlock,sizeof(DirParamBlock));
-
- dirBlock.header.clientData = (long)&callbackInfo;
- dirBlock.lookupGetPB.serverHint.aNet = 0;
- dirBlock.lookupGetPB.serverHint.aNode = 0;
- dirBlock.lookupGetPB.serverHint.aSocket = 0;
- dirBlock.lookupGetPB.dsRefNum = gAOCESetupDSRef;
- dirBlock.lookupGetPB.identity = gLocalIdentity;
- dirBlock.lookupGetPB.aRecordList = recordList;
- dirBlock.lookupGetPB.attrTypeList = attrList;
- dirBlock.lookupGetPB.recordIDCount = 1;
- dirBlock.lookupGetPB.attrTypeCount = 1;
- dirBlock.lookupGetPB.includeStartingPoint = false;
- dirBlock.lookupGetPB.getBuffer = buff;
- dirBlock.lookupGetPB.getBufferSize = kAttrBufferSize;
- dirBlock.lookupGetPB.startingRecordIndex = 1;
- dirBlock.lookupGetPB.startingAttrTypeIndex = 1;
- dirBlock.lookupParsePB.eachRecordID = RecordIDCallback;
- dirBlock.lookupParsePB.eachAttrType = nil;
- dirBlock.lookupParsePB.eachAttrValue = SingleAttrValueCallback;
- OCESetCreationIDtoNull(&dirBlock.lookupGetPB.startingAttribute.cid);
-
- err = DirLookupGet(&dirBlock,false);
- if (err==noErr || err==kOCEMoreData)
- err = DirLookupParse(&dirBlock,false);
-
- DisposPtrChk(buff);
- return err;
- }
-
-
- pascal Boolean RecordIDCallback(long clientData, const RecordID *recordID)
- {
- #pragma unused (clientData,recordID)
-
- return false;
- }
-
-
- pascal Boolean SingleAttrValueCallback(long clientData, const Attribute *attribute)
- {
- AttributeCopyInfoPtr callbackInfo;
- Size moveSize;
-
- callbackInfo = (AttributeCopyInfoPtr) clientData;
-
- moveSize = attribute->value.dataLength;
- if (moveSize>callbackInfo->bufferSize)
- moveSize = callbackInfo->bufferSize;
- BlockMove(attribute->value.bytes,callbackInfo->buffer,moveSize);
- return false;
- }
-
-
- pascal Boolean AddSlotCallback(long clientData, const Attribute *attribute)
- {
- #pragma unused (clientData)
- SlotSpec *newSlot;
- PackedRecordIDPtr packedRID;
- RecordID rid;
-
- newSlot = &gSlotDatabase[gNumSlots++];
-
- packedRID = (PackedRecordIDPtr) attribute->value.bytes;
- OCEUnpackRecordID(packedRID,&rid);
- OCECopyCreationID(&rid.local.cid,&newSlot->slotCID);
-
- return false; // continue
- }
-
-
- pascal Boolean SearchCallback(long clientData, const Attribute *attribute)
- {
- AttributeCopyInfoPtr callbackInfo;
- PackedRecordIDPtr packedRID;
- RecordID rid;
- short checkMatch;
-
- callbackInfo = (AttributeCopyInfoPtr)clientData;
-
- if (callbackInfo->bufferSize!=attribute->value.dataLength)
- return false; // continue the search- lengths don't match
-
- for (checkMatch=0; checkMatch<callbackInfo->bufferSize; checkMatch++) {
- if (attribute->value.bytes[checkMatch]!=callbackInfo->buffer[checkMatch])
- return false; // continue the search
- }
-
- // copy the attribute CID
-
- OCECopyCreationID(&attribute->cid,&callbackInfo->cid);
-
- return true; // end the search - we found the match
- }
-
-
- // AddAttribute
- //
- // add an attribute value to a record
- //
- // note: this can be can be called in response to an EPPC handled by the "alternate" event loop
- // this is why we
- OSErr AddAttribute(CreationID *cid,short dsRef,AttributeType *attribType,void *data,unsigned long dataLength,AttributeTag tag)
- {
- DirParamBlock attrBlock;
- RecordID recordID;
- Attribute attribute;
- OSErr err;
-
- MakePersonalRecordID(&recordID,cid);
-
- attrBlock.addAttributeValuePB.serverHint.aNet = 0;
- attrBlock.addAttributeValuePB.serverHint.aNode = 0;
- attrBlock.addAttributeValuePB.serverHint.aSocket = 0;
- attrBlock.addAttributeValuePB.dsRefNum = dsRef;
- attrBlock.addAttributeValuePB.identity = gLocalIdentity;
- attrBlock.addAttributeValuePB.aRecord = &recordID;
- attrBlock.addAttributeValuePB.attr = &attribute;
-
- ClearBuffer(&attribute,sizeof(Attribute));
- err = OCECopyRString((const RString *)attribType, (RString *)&attribute.attributeType,kAttributeTypeMaxBytes);
- if (err!=noErr)
- return err;
- attribute.value.tag = tag;
- attribute.value.dataLength = dataLength;
- attribute.value.bytes = (Ptr)data;
- err = DirAddAttributeValue(&attrBlock,false);
-
- return err;
- }
-
-
- // GetSingleRecord
- //
- // returns the RecordID for a record of a given type, if it matches the name you were
- // looking for which is passed in
- //
- OSErr GetSingleRecord(short dsRef,RStringPtr recordType,RStringPtr recordName,CreationID *returnedCID)
- {
- DirParamBlock dirBlock;
- Ptr buff;
- char *blank;
- RStringPtr typesList[1];
- OSErr err;
- RecordCopyInfo recCopyInfo;
-
- buff = NewPtr(kRecordBufferSize);
- if (MemError()!=noErr)
- return MemError();
-
- for (blank=(char*)&dirBlock;(blank-(char*)&dirBlock)<sizeof(DirParamBlock);blank++)
- *blank=0;
-
- typesList[0] = recordType;
-
- recCopyInfo.gotOne = false;
- recCopyInfo.creationID = returnedCID;
- recCopyInfo.recordName = recordName;
-
- dirBlock.enumerateGetPB.serverHint.aNet = 0;
- dirBlock.enumerateGetPB.serverHint.aNode = 0;
- dirBlock.enumerateGetPB.serverHint.aSocket = 0;
- dirBlock.enumerateGetPB.dsRefNum = dsRef;
- dirBlock.enumerateGetPB.identity = gLocalIdentity;
- dirBlock.enumerateGetPB.clientData = (long)&recCopyInfo;
- dirBlock.enumerateGetPB.aRLI = nil;
- dirBlock.enumerateGetPB.startingPoint = nil;
- dirBlock.enumerateGetPB.sortBy = kSortByName;
- dirBlock.enumerateGetPB.sortDirection = kSortForwards;
- dirBlock.enumerateGetPB.typesList = typesList;
- dirBlock.enumerateGetPB.typeCount = 1;
- dirBlock.enumerateGetPB.enumFlags = kEnumDistinguishedNameMask;
- dirBlock.enumerateGetPB.includeStartingPoint = false;
- dirBlock.enumerateGetPB.getBuffer = buff;
- dirBlock.enumerateGetPB.getBufferSize = kRecordBufferSize;
- dirBlock.enumerateParsePB.eachEnumSpec = GatewayDirEnumCallback;
-
- err = DirEnumerateGet(&dirBlock,false);
- if (err==noErr || err==kOCEMoreData)
- err = DirEnumerateParse(&dirBlock,false);
-
- DisposPtr(buff);
-
- if ((err==noErr) && !recCopyInfo.gotOne)
- err = kNoRecords;
-
- return err;
- }
-
-
- pascal Boolean GatewayDirEnumCallback(long clientData, const DirEnumSpec *enumSpec)
- {
- RecordCopyInfo *recCopyInfo;
-
- recCopyInfo = (RecordCopyInfo *)clientData;
-
- if (!recCopyInfo->gotOne && enumSpec->enumFlag & kEnumDistinguishedNameMask &&
- OCEEqualRString(recCopyInfo->recordName,enumSpec->u.recordIdentifier.recordName,kOCERecordOrDNodeName)) {
-
- OCECopyCreationID(&enumSpec->u.recordIdentifier.cid,recCopyInfo->creationID);
- recCopyInfo->gotOne = true;
- return true;
-
- }
- return false;
- }
-
-
- OSErr DeleteSingleAttributeValue(CreationID *cid,AttributeTypePtr attrType,
- AttributeCreationID *attrCID)
- {
- DirParamBlock dirBlock;
- RecordID recordID;
- OSErr err;
- Attribute attribute;
-
- MakePersonalRecordID(&recordID,cid);
- OCECopyRString((RStringPtr)attrType,(RStringPtr)&attribute.attributeType,kAttributeTypeMaxBytes);
- OCECopyCreationID(attrCID,&attribute.cid);
-
- ClearBuffer(&dirBlock,sizeof(DirParamBlock));
-
- dirBlock.deleteAttributeValuePB.dsRefNum = gAOCESetupDSRef; // working with PAB
- dirBlock.deleteAttributeValuePB.identity = gLocalIdentity;
- dirBlock.deleteAttributeValuePB.aRecord = &recordID;
- dirBlock.deleteAttributeValuePB.attr = &attribute;
- err = DirDeleteAttributeValue(&dirBlock,false);
- return err;
- }
-
-
- void CloseGatewayStuff(void)
- {
- }
-
-
- OSErr InitAOCEStuff(void)
- {
- return noErr;
- }
-
-
- void CloseAOCEStuff(void)
- {
- ExitAuthStuff();
- }
-
-
- // HasAOCE
- //
- // returns true only of AOCE is available and running
- //
- Boolean HasAOCE(void)
- {
- OSErr err;
- long response;
-
- if (!TrapAvailable(_GestaltDispatch))
- return false;
-
- err = Gestalt(gestaltOCEToolboxAttr,&response);
- if (err!=noErr)
- return false;
-
- return (response && (response << gestaltOCETBAvailable));
- }
-
-
- // MakePersonalRecordID
- //
- // this convenience function fills in a RecordID given a creation ID, since a fully specified
- // record ID for a personal directory contains only a creation ID
- //
- void MakePersonalRecordID(RecordID *recordID,CreationID *creationID)
- {
- recordID->rli = nil;
- OCECopyCreationID(creationID,&recordID->local.cid);
- recordID->local.recordName = nil;
- recordID->local.recordType = nil;
- }
-
-
- // GetSlotSpecFromID
- //
- // given a slot ID this function returns the corresponding slot info record which we store
- // for each slot
- //
- SlotSpec *GetSlotSpecFromID(short slotID)
- {
- short index;
-
- for (index=0; index<gNumSlots; index++)
- if (gSlotDatabase[index].slotID==slotID)
- return &gSlotDatabase[index];
-
- return nil;
- }
-
-
- void MacToMailTime(unsigned long macTime,MailTime *mailTime)
- {
- mailTime->time = macTime + (8*60*60); // assume pacific time for now (hack)
- mailTime->offset = (8*60*60);
- }
-
-
- void r2cString (RString *rStr,char *cStr)
- {
- StringPtr pStr;
-
- pStr = OCERToPString(rStr);
- pstrcpy((StringPtr)cStr,pStr);
- p2cstr((StringPtr)cStr);
- }
-
-
- // this routine copies as many bytes from rstr1 to rstr2, truncating rstr2 if there isn't
- // enough space
- //
- OSErr OCECopyFitRString (RString *str1, RString *str2, unsigned short str2Length)
- {
- unsigned short saveLength;
- OSErr err;
-
- saveLength = str1->dataLength;
-
- if (str2Length<str1->dataLength)
- str1->dataLength = str2Length;
-
- err = OCECopyRString(str1,str2,str2Length);
- str1->dataLength = saveLength;
- return err;
- }
-
-
- pascal void MSAMCompletion(MSAMParam *gwp)
- {
- long saveA5;
-
- saveA5 = SetA5((long)gwp->header.saveA5);
-
- gWakeUpSecondary = true; // set a flag in case we get caught
- WakeUpProcess(&gOurPSN); // in between check and WaitNextEvent...
-
- SetA5(saveA5);
- }
-